/**
 * @fileOverview
 * WashizuMahjong.
 * http://code.google.com/p/washizu-mahjong/
 */
/**
 * @constructor
 * @public
 * @this {Player}
 * @param {String} [playerName="Unknown"] The variable to set as player's name.

 * @property {number} score Player's score.
 * @property {MahjongTile[]} hand
 * @property {MahjongTile[]} discardTiles 棄牌
 * @property {Boolean} [isRichi=false] 立直（聽）
 * @property {Boolean} [isDoubleRichi=false] W立直
 * @property {Boolean} [isDoujun=false] 同巡
 * @property {number} [richiTurn=-1] The turn when the player richi, it is used to check 一發.
 * @property {Boolean} [canOneShotTurn=false] Whether the player can 一發
 * @property {Meld[]} meld 出示牌
 * @property {MahjongTile} pickingUpTile
 * @property {Boolean} isDoorClear <!-- readonly --> Whether the player is door clear
 */
function Player(playerName){
	this["name"] = playerName? playerName.toString() : "Unknown";
	this.hand=[];
	this.discardTiles = [];
	this.meld = [];
}
/**
 * Player's name, it will be set as "playerName"
 * @type String
 */
Player.prototype.name = "";
Player.prototype.score = 0;
Player.prototype.hand = null;
Player.prototype.discardTiles = null;
Player.prototype.meld = null;
Player.prototype.isRichi= false;
Player.prototype.isDoubleRichi= false;	//雙立直
Player.prototype.isDoujun = false;	//同巡
Player.prototype.richiTurn = -1;	//一發
Player.prototype.canOneShotTurn = false;
Player.prototype.pickingUpTile = null;
/**
 * If the player makes a meld by calling for another player's discard, then this variable become true.
 * It will go back to false if the player discard a tile.
 * @type Boolean
 * @see Player#toDiscardTile
 */
Player.prototype.callingOthers = false;

Player.prototype.toString = function(){
	return "[Player " + this["name"] + "\tScore: " + this["score"] + "]";
}
/*
 * Whether the hand of the player is door clear
 */
Player.prototype.__defineGetter__("isDoorClear", function(){
	if(this.meld.length === 0)
		return true;
	var All_Closed_Quad = true;
	this.meld.forEach(function(val, index, arr){
		if(!All_Closed_Quad)
			return;
		if(val.type != MeldType.KONG ||
		   !(val.kongType == KongType.Closed_Quad_from_Hand || val.kongType == KongType.Closed_Quad_from_PickupTile))
			All_Closed_Quad = false;
	});
	return All_Closed_Quad;

});
/*
 * Play ID.
 */
Player.prototype.__defineGetter__("id", function(){
	return players.indexOf(this);
});
/*
 * 自風
 */
Player.prototype.__defineGetter__("wind", function(){
	return (players.indexOf(this) + players.length - RoundDetail.dealer) % players.length
});
/**
 * To check whether the hand is ready
 * @return  {MahjongTile[]} The tiles which is superfluous for ready hand.
 */
Player.prototype.__defineGetter__("isReadyHand", function(){
	var thisHand = this.hand;
	var h = thisHand.slice(0, thisHand.length);
	h.push(this.pickingUpTile)
	var removeableTiles = [];
	h.forEach(function(/*MahjongTile*/val, index, arr){
		var cloneArr = arr.slice(0, arr.length);
		cloneArr.removeByIndex(index);
		var handresult = Hand.countMeldsInHand(cloneArr);
		handresult.forEach(function(/*{HandInfo}*/_val, _index, _arr){
			if(removeableTiles.indexOf(val) < 0 && Hand.isReadyHand(_val)){
				removeableTiles.push(val);
			}
		});
	});

	return removeableTiles;
});
/**
 * @function
 * @param {MahjongTile} newTile Optional.
 * @see KongType
 * @return  {Number}
 */
Player.prototype.canKong = function(newTile){
	if(this.hand.length < 4) return KongType.None;
	var h = this.hand;
	if(newTile){
		//暗槓
		var i;
		//Kong Tile is the picking up tile.
		for(i= 0; i < h.length - 2; i++){
			if(h[i].isSameTile(newTile) && h[1+i].isSameTile(newTile) && h[2+i].isSameTile(newTile)){
				if(newTile == this.pickingUpTile)
					return KongType.Closed_Quad_from_PickupTile;
				else
					return KongType.Open_Quad;
			}
		}
		//加槓
		//player can kong tiles which has already pong
		for(i = 0; i < this.meld.length; i++){
			var m = this.meld[i];
			if(m.type == MeldType.PONG){
				if(m.takedTile.isSameTile(newTile))
					return KongType.Added_Open_Quad;
			}
		}
	}else{
		//kong tile is in the hand
		var _h = h.clone();
		if(this.pickingUpTile)
			_h.push(this.pickingUpTile);
		Hand.sort(_h);
		for(var i= 0; i < _h.length - 1; i++){
			if(i < _h.length - 4 &&
				_h[i].isSameTile(_h[1+i]) &&
				_h[i].isSameTile(_h[2+i]) &&
				_h[i].isSameTile(_h[3+i]))
				return KongType.Closed_Quad_from_Hand;
		}
	}
	return KongType.None;
}
/**
 * @function
 * @param {MahjongTile} newTile
 * @return  {Boolean}
 */
Player.prototype.canPong = function(newTile){
	if(this.hand.length < 3) return false;
	var h = this.hand;
	for(var i= 0; i < h.length - 1; i++){
		if(h[i].isSameTile(newTile) && h[1+i].isSameTile(newTile))
			return true;
	}
	return false;
}
/**
 * @function
 * @param {MahjongTile} newTile
 * @return  {Boolean}
 */
Player.prototype.canChow = function(newTile){
	if(this.hand.length < 3) return false;
	var h = this.hand;
	for(var i= 1; i < h.length - 1; i++){
		if(h[i].isSameTileType(newTile)){
			try{
			if(h[i+1].isSameTileType(newTile) && h[i+1].simpleNum == newTile.simpleNum+1 &&
			   h[i+2].isSameTileType(newTile) && h[i+2].simpleNum == newTile.simpleNum+2)
				return true;
			}catch(e){}
			if(h[i+1].isSameTileType(newTile) && h[i+1].simpleNum == newTile.simpleNum+1 &&
			   h[i-1].isSameTileType(newTile) && h[i-1].simpleNum == newTile.simpleNum-1)
				return true;
			try{
			if(h[i-1].isSameTileType(newTile) && h[i-1].simpleNum == newTile.simpleNum-1 &&
			   h[i-2].isSameTileType(newTile) && h[i-2].simpleNum == newTile.simpleNum-2)
				return true;
			}catch(e){}
		}
	}
	return false;
}
/**
 * Clean all tiles in a player's hand
 * @see Game-startNewRound
 */
Player.prototype.clean = function(){
	this.hand=[];
	this.discardTiles = [];
	this.meld = [];
	this.isRichi=!1;
	this.isDoubleRichi=!1;	//雙立直
	this.isDoujun = !0;
	this.richiTurn = -1;	//一發
	this.canOneShotTurn = false;
	this.pickingUpTile=null;
	this.callingOthers = false;
}
/**
 * Pick up new tile
 * @see gameTiles
 * @see Player#pickingUpTile
 * @return {ActionMessage}
 */
Player.prototype.pickUp= function(){
	if(this.pickingUpTile || Game.turn != this.id ) return;
	if(this.callingOthers) return;
	if(RoundDetail.pickedUpTilesNum > bgColor="transparent"){
		//Round finish, Checking any winner
		return;
	}
	var index = parseInt(Math.random() * gameTiles.length);
	this.pickingUpTile = gameTiles[index];
	gameTiles.removeByIndex(index);
	var t = this;
	this.pickingUpTile.owner = {
		id: t.id,
		name: t.name
	};
	RoundDetail.pickedUpTilesNum++;
	this.pickingUpTile.pickingTileNum = RoundDetail.pickedUpTilesNum;
	return new ActionMessage(ActionMessage.PickUp, t, this.pickingUpTile)
}
/**
 *
 * @see Player#isReadyHand
 * @see Player#isRichi
 * @see Player#isDoubleRichi
 * @see Player#richiTurn
 * @see Player#canOneShotTurn
 * @return {ActionMessage|Boolean}
 */
Player.prototype.richiAction=function(){
	if(!this.isDoorClear ||
	   this.isReadyHand.length == 0 ||
	   this.isDoubleRichi || this.isRichi ||
	   this.score < 1000){
		return false;
	}
	if(RoundDetail.pickedUpTilesNum < players.length){
		//check whether player can W richi
		var canWRichi = true
		for(var i = this.index; i >= 0; i--){
			if(players[i].meld.length!=0){
				canWRichi = false;
				break;
			}
		}
		if(canWRichi){
			this.isDoubleRichi = true;
		}
	}
	this.isRichi = true;
	this.canOneShotTurn = true;
	this.richiTurn = RoundDetail.pickedUpTilesNum;
	this.score -= 1000;
	return new ActionMessage(ActionMessage.Richi, this, null, null,{isDoubleRichi:this.isDoubleRichi});
}
/**
 * To discard a tile from hand or the picking up tile
 * @param {MahjongTile|number} [d=pickingUpTile]. <br />
 * 	If it is a MahjongTile, it should be the tile in the hand or the picking up tile. <br/>
 * 	If d is number. It should be the index of the tile which is in the hand. Otherwise, if d is -1, it will be seen as the picking up tile.<br />
 * 	If d is null, then the program will set it as the pickingUpTile of the player.
 * @see Player#hand
 * @see Player#pickingUpTile
 * @see Hand.sort
 * @return {ActionMessage|Boolean}
 */
Player.prototype.toDiscardTile = function(d){
	if(!this.pickingUpTile){
		return;
	}
	if(this.isRichi || this.isDoubleRichi){
		if(this.richiTurn == RoundDetail.pickedUpTilesNum){
			if(this.isReadyHand.indexOf(d) < 0){
				// in richi turn, still can discard any tiles.
				return false;
			}else{
				d.isRichiTile = true;
			}
		}else{
			d = this.pickingUpTile;
		}
	}
	if(typeof d == "object" && d != this.pickingUpTile && this.hand.indexOf(d) == -1)return;
	if(typeof d == "number"){
		if(d >= this.hand.length) return;
		if(d >= 0){
			d = this.hand[d];
		}else{
			d = this.pickingUpTile;
		}
	}
	d = d || this.pickingUpTile;

	this.discardTiles.push(d);
	RoundDetail.discardTiles.push(d);
	if(d == this.pickingUpTile){
		this.pickingUpTile = null;
	}else{
		this.hand.remove(d);
		this.hand.push(this.pickingUpTile);
		this.pickingUpTile = null;
		Hand.sort(this.hand);
	}
	this.callingOthers = 0;
	//To run AI
	//check whether can pong / kong / chow / agari
	var nt = Game.turn;
	for(var i = (Game.turn + 1) % players.length; i != Game.turn; i = (i + 1) % players.length){
		players[i].run();
		if(nt != Game.turn){
			players[Game.turn].run();
			return;
		}
	}
	//if not finish round, run next player's AI
	Game.turn = (Game.turn + 1) % players.length;

	ActionMessage(ActionMessage.Discard, this, d, null, (d.isRichiTile? {isRichiTile: true}: null) );

	if(RoundDetail.pickedUpTilesNum == bgColor="transparent"){
		Game.drawRound();
	}else{
		players[Game.turn].run();
	}
}
/**
 * @see Player#canPong
 * @param {MahjongTile}pongTile
 * @return {ActionMessage}
 */
Player.prototype.pongAction=function(pongTile){
	if(!this.canPong(pongTile))return;
	var t = this;
	pongTile.taker = {
		id: t.id,
		name: t.name
	};

	var h = [];
	this.hand.forEach(function(val,index, arr){
		if(val.isSameTile(pongTile)){
			h.push(val);
		}
	});
	h.forEach(function(val,index, arr){
		if(val.isSameTile(pongTile)){
			t.hand.remove(val);
		}
	})

	var a = new Meld(MeldType.PONG, pongTile, h)
	this.meld.push(a)
	Game.turn = this.id;
	this.callingOthers = 1;

	//remove each person's OneShotTurn
	for(var _ = 0; _ < players.length; _++){
		if(players[_]== this) continue;
		players[_].canOneShotTurn=false;
	}
	this.makeDoujun();
	//when this function run, turn  is not this.
	Game.turn = this.id;
	
	return new ActionMessage(ActionMessage.Pong, this, pongTile, players[pongTile.owner]);
}
/**
 * @see Player#canKong
 * @see Dora.addDora
 * @see KongType
 * @param {MahjongTile}t
 */
Player.prototype.kongAction=function(t){
	var canKong = this.canKong(t);
	if(!canKong)return;
	if(RoundDetail.kongTimes > 4) return;
	var theTileIsFromHand = (t == this.pickingUpTile || this.hand.indexOf(t) >= 0);
	//set up details of the taker
	var _this = this;
	t.taker = {
		id: _this.id,
		name: _this.name,
		is_self: theTileIsFromHand
	};
	switch(canKong){
		case KongType.Closed_Quad_from_Hand:
			this.hand.remove(t);
			if(this.pickingUpTile)
				this.hand.push(this.pickingUpTile);
		case KongType.Closed_Quad_from_PickupTile:
			this.pickingUpTile = null;
		case KongType.Open_Quad:
			var meld_tiles = [];
			this.hand.forEach(function(val,index, arr){
				if(t.isSameTile(val)){
					meld_tiles.push(val);
				}
			});

			meld_tiles.forEach(function(val,index, arr){
				_this.hand.remove(val);
			});

			var a = new Meld(MeldType.KONG, t, meld_tiles);
			a.kongType = canKong
			this.meld.push(a);
			break;
		case KongType.Added_Open_Quad:
			this.meld.forEach(function(val, index, arr){
				if(val.type == MeldType.PONG && val.meldTiles[0].isSameTile(t)){
					val.type = MeldType.KONG;
					val.addKongTile = t;
				}
			})
			break;
		case KongType.None:
			return;
	}

	Dora.addDora();
	RoundDetail.kongTimes++;


	this.callingOthers = true;
	//remove each person's OneShotTurn
	for(var _ = 0; _ < players.length; _++){
		if(players[_]== this) continue;
		players[_].canOneShotTurn=false;
	}
	
	this.makeDoujun();
	//when this function run, turn  is not this.
	Game.turn = this.id;

	//add a tile from Mountain.
	var index = parseInt(Math.random() * gameTiles.length);
	this.pickingUpTile = gameTiles[index];
	gameTiles.removeByIndex(index);
	this.pickingUpTile.owner = {
		id: _this.id,
		name: _this.name
	};
	this.pickingUpTile.isRinshanTile = true;	//set it is リーシャン tile

	
	ActionMessage(ActionMessage.Kong, _this, t, (t.owner == this.id ? null: players[t.owner]),{kongType: canKong});
	ActionMessage(ActionMessage.PickUp, _this, this.pickingUpTile, null, {isRinshanTile: true});
}
/**
 * @param {MahjongTile} t
 * @param {Number[]} $ The indexes which can be chowed with "t"
 * @see Player#canChow
 * @see Player#chowChoise
 * @see Player#meld
 * @return {ActionMessage|Boolean}
 */
Player.prototype.chowAction=function(t, $){
	if(!this.canChow(t) || !$ || !t || !(($ instanceof Array) && $.length == 2) )return false;
	var this_index = this.id
	if(players[ (this_index + 4-1) % players.length].discardTiles.lastItem() != t) return false;

	var _this = this;
	t.taker = {
		id: _this.id,
		name: _this.name
	};

	$.sort();
	var hand = this.hand, choise = this.chowChoise(t);
	for(var i = 0; i < choise.length; i++){
		choise[i].sort();
		if($[0] == choise[i][0] && $[1] == choise[i][1]){
			var a = new Meld(MeldType.CHOW, t, [hand[ $[0] ], hand[ $[1] ]])
			hand.removeByIndex($[1]);
			hand.removeByIndex($[0]);
			this.meld.push(a);
			Game.turn = this.id;
			this.callingOthers = true;

			//remove each person's OneShotTurn
			for(var _ = 0; _ < players.length; _++){
				if(players[_]== this) continue;
				players[_].canOneShotTurn=false;
			}
			
			this.makeDoujun();
			//when this function run, turn  is not this.
			Game.turn = this.id;
			return new ActionMessage(ActionMessage.Chow, _this, t, players[t.owner.id], {chowChoise: $});
		}
	}
	return false;
}
/**
 * To get the tiles which can be chow in hand.
 * @return {Number[][]} The number array for the indexes of the tiles which can be chowed with "t".<br />
	This array will like this : [[1,2],[2,3]]. <br />
 	In this example. It means that the tile "t" can be chowed with both hand[1] and hand[2] or both hand[2] and hand[3].<br />
	For instence
		<ul style="padding-left: 20px;list-style-type: circle;">
			<li>t is Bamboo 6(<span class="unicode">🀞</span>)</li>
			<li>hand[<span class="NUMB">1</span>] is Bamboo 4(<span class="unicode">🀜</span>)</li>
			<li>hand[<span class="NUMB">2</span>] is Bamboo 5(<span class="unicode">🀝</span>)</li>
			<li>hand[<span class="NUMB">3</span>] is Bamboo 7(<span class="unicode">🀟</span>).</li>
		</ul>
 * @see Player#canChow
 * @param {MahjongTile}t
 */
Player.prototype.chowChoise = function(t){
	if(!this.canChow(t))return false;
	var h = this.hand;
	var result = []
	for(var i= 1; i < h.length - 1; i++){
		if(h[i].isSameTileType(t)){
			try{
			if(h[i+1].isSameTileType(t) && h[i+1].simpleNum == t.simpleNum+1 &&
			   h[i+2].isSameTileType(t) && h[i+2].simpleNum == t.simpleNum+2)
				result.push([i+1],[i+2]);
			}catch(e){}
			if(h[i+1].isSameTileType(t) && h[i+1].simpleNum == t.simpleNum+1 &&
			   h[i-1].isSameTileType(t) && h[i-1].simpleNum == t.simpleNum-1)
				result.push([i-1,i+1]);
			try{
			if(h[i-1].isSameTileType(t) && h[i-1].simpleNum == t.simpleNum-1 &&
			   h[i-2].isSameTileType(t) && h[i-2].simpleNum == t.simpleNum-2)
				result.push([i-1,i-2]);
			}catch(e){}
		}
	}
	return result;
}
/**
 * @return {MahjongTile[]}	the tiles which other players can see.
 */
Player.prototype.getHand = function(){
	var h = this.hand.clone();
	for(var i = h.length-1; i > 0; i--){
		if(h[i].hiddenTile){
			h[i] = [hiddenTile].clone()[0];
		}
	}
	return h;
}
/**
 * @see Player#isDoujun
 */
Player.prototype.makeDoujun = function(){
	if(this.isDoujun){
		return true
	}
	if(Game.turn == this.id){
		return false
	}
	if(Score.yakuList(this, players[Game.turn].discardTiles.lastItem())){
		this.isDoujun = true;
		return true
	}
	return false
}
Player.prototype.skipAction = function(){
	
}
//AI
/*
 * The computer's AI.
 * AI is a function with one param variable. The variable means whether it is player's turn
 * @type function
 * @see Player#run
 */
Player.prototype.__defineSetter__("AI", function(AI){
	if(typeof AI != "function"){
		return;
	}
	prototype.__defineGetter__("AI", function(){return AI});
});
Player.prototype.__defineGetter__("AI", function(){return DEFAULT_AI});
/**
 * Running the AI of the player.
 */
Player.prototype.run = function(){
	if(Game.turn == this.id){
		if(Game.isRoundFinish){
			//If somebody win the round or the round is drew, then do not did any action
			return false
		}
		//It is the players' turn
		this.pickUp();
		//Run AI to choose the tile be discard
		this.AI();
	}else{
		//It is not player's turn. AI is used for choose whether chowing, konging, or ponging.
		this.AI();
	}
}
/**
 * A simple ai.
 * @this Player
 * @see Player#run
 * @see Player#AI
 * @see DataTransfer
 */
function DEFAULT_AI(){
	var _this = this;
	if(_this.id == Game.turn){
		// it is the players's turn
		var s = Score.yakuList(_this);
		if(s){
			DataTransfer.sendMessage({
				player: _this,
				action: ActionMessage.SelfDrawn
			});
			/*
			Game.countScore(s, _this, _this.pickingUpTile);
			alert(_this.name + " self-drawn.\n " + s.yakus + "\n" + s.score + "points.");
			Game.finishRound();
			*/
		}else{
			_this.toDiscardTile(_this.pickingUpTile);
		}
	}else{
		var d = players[Game.turn].discardTiles.lastItem();
		var s = Score.yakuList(_this, d);
		if(s){
			DataTransfer.sendMessage(new DataTransfer.data(_this, ActionMessage.Ron));
			//Game.countScore(s, _this, players[Game.turn])
			//alert(_this.name + " win.\n " + s.yakus + "\n" + s.score + "points.");
			//Game.finishRound();
		}else{
			if(_this.canKong(d)){
				DataTransfer.sendMessage(new DataTransfer.data(_this, ActionMessage.Kong));
			}
			if(_this.canPong(d)){
				DataTransfer.sendMessage(new DataTransfer.data(_this, ActionMessage.Pong));
				return true;
			}
			if(_this.canChow(d)){
				var chowlist = _this.chowChoise(d);
				
				DataTransfer.sendMessage(new DataTransfer.data(_this, ActionMessage.ActionMessage.Chow, chowlist));
				return true;
			}
			_this.skipAction();
		}
	}
	
}
